package controller;

import java.util.Stack;

import org.w3c.dom.Document;

import game.ChessBoard;
import game.GameState;
import game.GameStateListener;
import game.Piece;

/**
 * <p>This class handles the undoing and redoing of moves in a game of chess.</p>
 * 
 * <p>In general, a move that has been undone can be redone. However, once a 
 * new state is saved to be undone, the redo stack is cleared.</p>
 * 
 * @author Mark Kelly
 * @date March 30, 2010
 * @version 4.0
 *
 */
public class UnRedoer implements GameStateListener {	
	// Instance Variables
	Document currentState;
	Stack<Document> undoStack;
	Stack<Document> redoStack;
	
	/**
	 * Creates a new instance of UnRedoer with no saved states.
	 */
	public UnRedoer() {
		currentState = null;
		undoStack = new Stack<Document>();
		redoStack = new Stack<Document>();
	}
	
	/**
	 * Returns the previous state.
	 * 
	 * @return a {@code org.w3c.dom.Document} object that contains the previous saved state
	 */
	public Document undo() {
		// Make sure that the stack is not empty
		if (!undoStack.isEmpty()) {
			redoStack.push(currentState);
			currentState = undoStack.pop();
			return currentState;
		}
		else {
			return null;
		}
	}
	
	/**
	 * Returns a state that was previously undone.
	 * 
	 * @return <p>a {@code org.w3c.dom.Document} object that contains the saved state or {@code null} if there is no previous state</p>
	 */
	public Document redo() {
		// Make sure that the stack is not empty
		if (!redoStack.isEmpty()) {
			undoStack.push(currentState);
			currentState = redoStack.pop();
			return currentState;
		}
		else {
			return null;
		}
	}	
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void gameStarted(GameState gs) {
		System.out.println("gameStarted");
		undoStack.clear();
		currentState = xml.XMLHandler.saveToDocument(gs, "Chess");
		saveUndo(gs);
	}

	/**
	 * {@inheritDoc}
	 * 
	 * <p>UnRedoer does  not implement {@code needPieceChoice()}.</p>
	 */
	@Override
	public void needPieceChoice(GameState gs) {
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void pieceMoved(GameState gs, Piece p, ChessBoard b) {
		System.out.println("pieceMoved");
		saveUndo(gs);
	}

	/**
	 * {@inheritDoc}
	 * 
	 * <p>UnRedoer does not implement {@code stateChanged()}.</p>
	 */
	@Override
	public void stateChanged(GameState gs) {
		/*
		 * In general, stateChenged is called when a non-undoable action is 
		 * performed. The possible exception is when ClassicChess promotes a 
		 * Pawn. Allowing promotion would create other problems anyways.
		 */
	}
	
	/**
	 * Save a state so that it can be undone later.
	 * 
	 * @param state the {@code GameState} to be saved
	 */
	private void saveUndo(GameState state) {
		undoStack.push(currentState);
		currentState = xml.XMLHandler.saveToDocument(state, "Chess");
		redoStack.clear();
	}
}
